#include "WideTextFinder.h"

#define NO_SKIP_CHAR  1  // a magic char not found in a valid numeric field

// private function to read characters from stream
// the constructors allow one and only one of the streams to be initialized
char WideTextFinder::read()
{
     char r;
     startMillis = millis();
     if (nSerialStream != NULL) {
          while(millis() < (startMillis + timeout)) {
               if (nSerialStream->available() > 0) {
                    r=nSerialStream->read();
                    //if(debug)
                    //Serial.print(r);
                    return r;
               }
          }
     }
     return 0;     // 0 indicates timeout
}

// constructors
//default timeout is 5 seconds

WideTextFinder::WideTextFinder(SoftwareSerial &stream, int timeout) :
     nSerialStream(&stream)
{
     this->timeout = timeout * 1000L;
     debug=true;
}

void WideTextFinder::setDebug(boolean d)
{
     debug=d;
}

// public methods
//
// find returns true if the target string is found
boolean  WideTextFinder::find(char *target)
{
     return findUntil(target, NULL);
}

// as above but search ends if the terminate string is found
boolean  WideTextFinder::findUntil(char *target, char *terminate)
{
     byte targetLen = strlen(target);
     byte index = 0;  // maximum target string length is 255 bytes
     byte termIndex = 0;
     byte termLen = (terminate == NULL ? 0 : strlen(terminate));
     char c;

     if( *target == 0)
          return true;   // return true if target is a null string
     while( (c = read()) != 0) {
          if( c == target[index]) {
               if(++index >= targetLen) { // return true if all chars in the target match
                    return true;
               }
          } else {
               index = 0;  // reset index if any char does not match
          }
          if(termLen > 0 && c == terminate[termIndex]) {
               if(++termIndex >= termLen)
                    return false;       // return false if terminate string found before target string
          } else
               termIndex = 0;
     }
     return false;
}

// places the string between the prestring  and poststring in the given buffer
// buffer must be one more than the longest string to get
// the string will be truncated to fit the buffer length
// end of string determined by a single character match to the first char of poststring
// returns the number of characters placed in the buffer (0 means no valid data found)
int WideTextFinder::getString( char *pre_string, char *post_string, char *buffer, int length)
{
     if( find(pre_string) ) {
          int index = 0;
          *buffer = 0;
          while(index < length-1 ) {
               char c = read();
               if( c == 0 ) {
                    return 0;   // timeout returns 0 !
               } else if( c == *post_string ) {

                    while (index < length) {
                         buffer[index] = '\0'; // terminate the string !!!!!!!!!!!IT DOESN'T WORK!!!!!!!!!!!!!!!!!
                         index++;
                    }

                    //buffer[index]=0;
                    return index;               // data got successfully
               } else {
                    buffer[index++] = c;
               }
          }
          buffer[index] = 0;
          return index; // Note: buffer full before the closing post_string encountered
     }
     return 0;    //failed to find the prestring
}

// getValue method:
// returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// function is terminated by the first character that is not a digit.
long WideTextFinder::getValue()
{
     return getValue(NO_SKIP_CHAR); // terminate on first non-digit character
}

// as above but a given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
long WideTextFinder::getValue(char skipChar)
{
     boolean isNegative = false;
     long value = 0;
     char c;

     while( (c = read()) != '-' && (c < '0' || c > '9') && (c!=0) )
          ;// ignore non numeric leading characters
     do {
          if(c == skipChar )
               ; // ignore this charactor
          else if (c==0) // Timeout
               return 0;
          else if(c == '-')
               isNegative = true;
          else if(c >= '0' && c <= '9')        // is c a digit?
               value = value * 10 + c - '0';
          c = read();
     } while( (c >= '0' && c <= '9')  || c == skipChar );
     if(isNegative)
          value = -value;
     return value;
}

// float version of getValue method:
// as integer version but returns a floating point value
float WideTextFinder::getFloat()
{
     getFloat(NO_SKIP_CHAR);
}

// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float WideTextFinder::getFloat(char skipChar)
{
     boolean isNegative = false;
     boolean isFraction = false;
     long value = 0;
     float fValue;
     char c;
     float fraction = 1.0;

     while( (c = read()) != '-' && (c < '0' || c > '9') )
          ; // ignore non numeric leading characters
     do {
          if(c == skipChar)
               ; // ignore
          else if(c == '-')
               isNegative = true;
          else if (c == '.')
               isFraction = true;
          else if(c >= '0' && c <= '9')  {      // is c a digit?
               value = value * 10 + c - '0';
               if(isFraction)
                    fraction *= 0.1;
          }
          c = read();
     } while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar );
     if(isNegative)
          value = -value;
     if(isFraction)
          return value * fraction;
     else
          return value;
}


// returns the number of seconds to wait for the next char before aborting read
unsigned long WideTextFinder::getTimeout()
{
     return timeout;
}

// set a new value for the wait timeout
void WideTextFinder::setTimeout(unsigned long timeout)
{
     this->timeout = timeout * 1000L;
}
